/**
 * @file Matrix.h
 * @author chenwei (3190104818@zju.edu.cn)
 * @brief The class definition and clarification of Matrix using template. 
 * @version 0.1
 * @date 2021-10-06
 * 
 * @copyright Copyright (c) 2021
 * 
 */
#ifndef MATRIX_H
#define MATRIX_H
#include<iostream>
#include<cassert>
#include<cstring>

/**
 * @brief Matrix with variable sizes. 
 * 
 * @tparam T 
 */
template<typename T>
class Matrix{
public:
    Matrix(int row,int col):n_col(col), n_row(row)
    {
        m_matrix = new T[n_col*n_row];
    }
    Matrix(const Matrix& c)
    {
        n_col = c.n_col;
        n_row = c.n_row;
        m_matrix = new T[n_row * n_col];
        memcpy(m_matrix, c.m_matrix, n_row * n_col * sizeof(T));
        //实现深拷贝
    }
    bool is_square(){return n_row==n_col;}
    const T& operator()(int i,int j) const
    {
        assert(i >= 0 && i < n_row);
        assert(j >= 0 && j < n_col);
        return m_matrix[j * n_row + i];
    }
    T& operator()(int i,int j)
    {
        assert(i >= 0 && i < n_row);
        assert(j >= 0 && j < n_col);
        return m_matrix[j * n_row + i];
    }
    int getNRow() const{ return n_row; }
    int getNCol() const{ return n_col; }
    ~Matrix(){
        delete []m_matrix;
    }
    void print(){
        for (int i = 0; i < n_row;i++)
        {
            if (i==0)
                std::cout << "[\t";
            else
                std::cout << "\t";
            for (int j = 0; j < n_col; j++)
            {
                if(j!=0)
                    std::cout << ", ";
                std::cout << (*this)(i, j);
            }
            if(i<n_row-1)
                std::cout << std::endl;
            else
                std::cout << "\t]" << std::endl;
        }
    }
protected:
    T* m_matrix;
private:
    int n_row = 1;
    int n_col = 1;
};

/**
 * @brief 2*2 float matrix 
 * 
 */
class Matrix2f:public Matrix<float>
{
public:
    Matrix2f(float a11,float a12,
            float a21,float a22):Matrix<float>(2,2)
            {
                (*this)(0, 0) = a11;
                (*this)(0, 1) = a12;
                (*this)(1, 0) = a21;
                (*this)(1, 1) = a22;
            }
};

/**
 * @brief 3*3 float matrix 
 * 
 */
class Matrix3f:public Matrix<float>
{
public:
    Matrix3f(float a11,float a12,float a13,
             float a21,float a22,float a23,
             float a31,float a32,float a33) : Matrix<float>(3, 3)
             {
                 (*this)(0, 0) = a11;
                 (*this)(0, 1) = a12;
                 (*this)(0, 2) = a13;
                 (*this)(1, 0) = a21;
                 (*this)(1, 1) = a22;
                 (*this)(1, 2) = a23;                
                 (*this)(2, 0) = a31;
                 (*this)(2, 1) = a32;
                 (*this)(2, 2) = a33;

             }
    Matrix3f():Matrix<float>(3,3)
    {
        for (int i = 0; i < 9; i++)
        {
            this->m_matrix[i] = rand() / float(RAND_MAX);
        }
    }
};

/**
 * @brief Row vector and also 1*n matrix 
 * 
 * @tparam T 
 */
template<typename T>
class RowVector : public Matrix<T>
{
public:
    RowVector(int n):Matrix<T>(1,n){};
    const T& operator[](int i)const
    {
        assert(i >= 0);
        assert(i < this->getNCol());
        return this->m_matrix[i];
    }
    T& operator[](int i)
    {
        assert(i >= 0);
        assert(i < this->getNCol());
        return this->m_matrix[i];
    }
};

/**
 * @brief Collumn vector and also n*1 matrix 
 * 
 * @tparam T 
 */
template<typename T>
class ColVector : public Matrix<T>
{
public:
    ColVector(int n):Matrix<T>(n,1){};
    const T& operator[](int i) const
    {
        assert(i >= 0);
        assert(i < this->getNRow());
        return this->m_matrix[i];
    }
    T& operator[](int i)
    {
        assert(i >= 0);
        assert(i < this->getNRow());
       return this->m_matrix[i];
    }
};

/**
 * @brief 矩阵乘法
 * 
 * @tparam T 
 * @param A 
 * @param B 
 * @return Matrix<T> 
 */
template<typename T>
Matrix<T> operator*(const Matrix<T>& A,const Matrix<T>& B)
{
    if (A.getNCol() != B.getNRow()) {
        std::cerr << "Imcampatible size for matrix multiplication." << std::endl;
        return Matrix<T>(1,1);
    }
    int n_row = A.getNRow(), n_col = B.getNCol();
    Matrix<T> output(n_row, n_col);
    for (int i = 0; i < n_row;i++)
    {
        for (int j = 0; j < n_col;j++)
        {
            output(i, j) = 0;
            for (int k = 0; k < A.getNCol();k++)
            {
                output(i, j) += A(i, k) * B(k, j);
            }
        }
    }
    return output;
}

/**
 * @brief 行向量*列向量
 * 
 * @tparam T 
 * @param RV 
 * @param CV 
 * @return T 
 */
template <typename T>
T operator*(const RowVector<T>& RV,const ColVector<T>& CV )
{
    T result = 0;
    int size = RV.getNCol();
    assert(size == CV.getNRow());
    for (int i = 0; i < size;i++)
    {
        result += RV[i] * CV[i];
    }
    return result;
}

/**
 * @brief 列向量点积 
 * 
 * @tparam T 
 * @param RV 
 * @param CV 
 * @return T 
 */
template <typename T>
T operator*(const ColVector<T>& RV,const ColVector<T>& CV )
{
    T result = 0;
    int size = RV.getNRow();
    assert(size == CV.getNRow());
    for (int i = 0; i < size;i++)
    {
        result += RV[i] * CV[i];
    }
    return result;
}

/**
 * @brief 行向量点积 
 * 
 * @tparam T 
 * @param RV 
 * @param CV 
 * @return T 
 */
template <typename T>
T operator*(const RowVector<T>& RV,const RowVector<T>& CV )
{
    T result = 0;
    int size = RV.getNCol();
    assert(size == CV.getNCol());
    for (int i = 0; i < size;i++)
    {
        result += RV[i] * CV[i];
    }
    return result;
}
#endif